﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Entry;
using Cofe.Core.Filter;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using Cofe.Core.Service;
using Cofe.Core.Utils;

namespace Cofe.Core.Implements
{
    public class SearchActionProvider : PropertyProviderBase<IPropertyHost>, ISearchActionProvider
    {
        #region Factory
        [Export(typeof(ICofeService))]
        [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
        public class Factory : ISecondaryPropertyProviderFactory
        {
            public IEnumerable<IPropertyProvider> GetSecoondaryPropertyProviders(IPropertyHost propertyHost)
            {
                bool isSearch = propertyHost.Behaviors.IsPropertySupported(CofeEntryListProperties.FilterString);

                if (!(propertyHost.SecondaryPropertyProviders.Any((p) => p is ISearchActionProvider)) && isSearch)
                    yield return new SearchActionProvider(propertyHost, ServiceLocater.FindService<IEntryConstructor>());
            }


            private IServiceLocater _serviceLocater = null;
            public IServiceLocater ServiceLocater
            {
                get { return _serviceLocater ?? CofeServices.ServiceLocater; }
                set { _serviceLocater = value; }
            }

        }
        #endregion

        #region Constructor

        public SearchActionProvider(IPropertyHost propertyHost, IEntryConstructor entryConstructor)
            : base(propertyHost,
            propertyHost.SupportedDefinitions.Intersect(
             new IPropertyDefinitions[] {                  
                 Core.PropertyDefinitions.CofeDirDefinitions,
                 Core.PropertyDefinitions.CofeEntryListDefinitions 
             }, PropertyDefinitionComparer.CompareCategory).ToArray())
        {
            EntryConstructor = entryConstructor;

            registerActionAsync(CofeEntryListProperties.SearchAsync, (pm) => Task.Run(() => search(pm)));
            registerAction<IEnumerable<ICofeSystemInfo>>(CofeDirectoryProperties.List,
                (pm) => list(pm));

            registerProperties(CofeDirectoryProperties.HasSubFolders, () =>
            {
                if (!_searched) search(new ParameterDic());
                return list(new ParameterDic()).Any(p => p.Properties.SupportedDefinitions.Contains(
                    Cofe.Core.PropertyDefinitions.CofeDirDefinitions, PropertyDefinitionComparer.CompareCategory));
            });

            registerProperties(CofeDirectoryProperties.HasFiles, () =>
            {
                if (!_searched) search(new ParameterDic());
                return list(new ParameterDic()).Any(p => p.Properties.SupportedDefinitions.Contains(
                    Cofe.Core.PropertyDefinitions.CofeFileDefinitions, PropertyDefinitionComparer.CompareCategory));
            });
        }

        #endregion

        #region Methods

        protected virtual void search(ParameterDic pd)
        {
            IEntryFilter[] entryFilters; IOptionFilter[] optionFilters;
            string filterStr = this.Entity.Behaviors.GetProperty<string>(CofeEntryListProperties.FilterString);
            CofeServices.EntryFilterParser.ParseFilter(filterStr, out entryFilters, out optionFilters);

            var rootDirectories =
                    (entryFilters.Any(ef => ef is RootEntryFilter) ?
                    entryFilters.Where(ef => ef is RootEntryFilter && !ef.Negative)
                    .Select(ef => (ef as RootEntryFilter).DirectoryPropertyHost)
                    .Select(rph => CofeServices.EntryConstructor.ConstructEntry<ICofeDirectoryInfo>(rph)) :
                    CofeServices.VolumeRegistrar.Behaviors.GetRootDirectories(EntryConstructionFlags.Default)).ToArray();

            var expression = rootDirectories.SelectMany(rd => rd.EnumerateCofeSystemInfos(entryFilters, optionFilters,
                    EntryConstructionFlags.Default, CofeServices.EntryConstructor))
                    .Select(e => e.Properties)
                    .Distinct();

            expression = FilterUtils.UpdateOptionFilterExpressions(expression, optionFilters);
            _entryList = new ThreadSafeCachedEnumerable<IPropertyHost>(expression);
            _searched = true;
        }

        protected IEnumerable<ICofeSystemInfo> list(ParameterDic pd)
        {
            pd.IsHandled = true;            
            return from ph in _entryList select CofeServices.EntryConstructor.ConstructEntry(ph);
        }


        #endregion

        #region Data

        protected bool _searched = false;
        protected IEnumerable<IPropertyHost> _entryList = new List<IPropertyHost>();

        #endregion

        #region Public Properties

        /// <summary>
        /// For Debug only.
        /// </summary>
        public ISecondaryPropertyProviderFactory[] SecondaryPropertyProviderFactories { get; set; }

        /// <summary>
        /// For Debug only.
        /// </summary>
        public IPathParser[] PathParsers { get; set; }

        public IEntryConstructor EntryConstructor { get; internal set; }

        #endregion


    }
}
